-
Notifications
You must be signed in to change notification settings - Fork 346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use transaction-payment
's congestion modifier for Ethereum base-fee
#1765
Use transaction-payment
's congestion modifier for Ethereum base-fee
#1765
Conversation
The |
@AsceticBear The initial reason being having to maintain/re-implement less code/functionality which is quite easy to get wrong (e.g. fees will never recover or continuously drift in a single direction). |
A bit more detail: I explored using this and came to the conclusion that EIP1559's design isn't well suited for a network that doesn't experience consistent congestion. Basically, I think the original design creates a feedback mechanism that keeps the congestion modifier within a reasonable range, something like this:
Without this, it becomes critical to tune pallet While pallet |
/// This value is currently only used by pallet-transaction-payment as an assertion that the | ||
/// next multiplier is always > min value. | ||
pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); | ||
pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000); | ||
/// Maximum multiplier. We pick a value that is expensive but not impossibly so; it should act | ||
/// as a safety net. | ||
pub MaximumMultiplier: Multiplier = Multiplier::from(100_000u128); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we add a migration to initialize the FeeMultiplier
amount? Today it seems to be worth 1000000000000 on moonbase, moonriver in moonbeam, which is not in the expected range.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. I've explored this pretty thoroughly, here's my summary:
- For new blockchains, the genesis value will kick in (this PR made that possible)
- For existing blockchains, the minimum and maximum multipliers in
transaction-payment
will kick in immediately at the end of the runtime upgrade block
A migration could force the multiplier bounds to be enforced earlier (before extrinsics are charged fees for the runtime upgrade block), but this doesn't seem like a big deal to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To elaborate:
In transaction-payment
's on_finalize()
, the TargetedFeeAdjustment::convert()
will check the new min and max bounds and enforce them. This value will then be updated in pallet storage and will cause the next block to have fees within bounds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small questions but overall looks good
@@ -36,7 +36,7 @@ describeDevMoonbeam( | |||
(context) => { | |||
it("should have low balance transfer fees", async () => { | |||
const fee = await testBalanceTransfer(context); | |||
expect(fee).to.equal(20958001520875n); | |||
expect(fee).to.equal(79_359_001_520_875n); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to fetch this through a runtime API? I feel hardcoding this value is prone to having to change it every time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to avoid hard coding fee values everywhere, they break nearly all the time.
We have verifyBlockFees
, which could be suitable here. Note that it calculates the fees by hand, as opposed to asking the node/runtime to verify it, which I think is important for some of these tests.
Maybe we need an equivalent for a single extrinsic...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought there was a payment info runtime API that could return the expected fee, is that not suitable for this?
@@ -158,7 +159,7 @@ describeDevMoonbeamAllEthTxTypes("Precompiles - xtokens", (context) => { | |||
}); | |||
}); | |||
|
|||
describeDevMoonbeamAllEthTxTypes("Precompiles - xtokens", (context) => { | |||
describeDevMoonbeam("Precompiles - xtokens", (context) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intentional because it takes EIP-1559 by default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually takes legacy by default: https://github.com/PureStake/moonbeam/blob/24fddc3701e3e40c2f8febcc4ba1f0af7a49e144/tests/util/setup-dev-tests.ts#L81
I found this a bit surprising, but maybe it make a lot of sense to do it that way when we first introduced EIP1559.
Anyway, IIRC, the reason for this change is because the test uses the TRANSACTION_TEMPLATE
which uses gasPrice
instead of maxFeePerGas
/maxPriorityFeePerGas
. I'll rerun the test and see if I can figure out why exactly this was a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, so the problem is the way createTransaction
handles maxPriorityFeePerGas
, which I think could be improved (I'll open an internal issue to track that).
Basically, in the case of (1) EIP-1559 txn with (2) gasPrice
provided but no maxFeePerGas
, it will specify maxPriorityFeePerGas: 0
, which leads to a discrepancy in the exact fees charged between txn types:
export const createTransaction = async (
context: DevTestContext,
options: TransactionOptions
): Promise<string> => {
const isLegacy = context.ethTransactionType === "Legacy";
const isEip2930 = context.ethTransactionType === "EIP2930";
const isEip1559 = context.ethTransactionType === "EIP1559";
const gasPrice = options.gasPrice !== undefined ? options.gasPrice : DEFAULT_TXN_MAX_BASE_FEE;
const maxPriorityFeePerGas =
options.maxPriorityFeePerGas !== undefined ? options.maxPriorityFeePerGas : 0;
// is computed in frontier, but that's currently unavoidable. | ||
let min_gas_price = TransactionPayment::next_fee_multiplier() | ||
.saturating_mul_int(currency::WEIGHT_FEE.saturating_mul(WEIGHT_PER_GAS as u128)); | ||
(min_gas_price.into(), Weight::zero()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that we read TransactionPayment::next_fee_multiplier()
but we still account zero weight for the min_gas_price
call. Is next_fee_multiplier
expected to be cached and the weight in the min_gas_price
call context to be negligible? Or what is the reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this should be more than 0, will PR a fix soon :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@notlesh Think I vaguely recall us discussing this and you mentioned that since this is read way too often, we could offer this for free (since it's cached more or less). Not sure if that condition still applied.
…moonbeam-foundation#1765) * Convert transaction-payments congestion modifier to replace Ethereum's base-fee * Remove outdated comment * fix precision bug * import * use saturating mul, improve doc * update fee parameters, add tests * make tests similar * add tests to all runtimes * fix tests * Add transaction-payment GenesisConfig to initialize its Multiplier * Fix some moonbase tests * Rename FixedGasPrice -> TransactionPaymentAsGasPrice * fmt * More ts test fixes * Fixes staking locks * Fix existential balance tests * Fix length fee tests * Query base_fee in createTransaction when needed * Fixes gasPrice for existential test * Fix deposit/fee check for multiple deposit * Fix merge issue * Use legacy txns for some tests that expect full gas_price to be charged * Bump gas price for tests * Update tests to reflect createTransfer default * Use constant for DEFAULT_TXN_MAX_BASE_FEE * Reflect txn values to reflect base_fee change * Prefer constant over literal value * Overhaul fee calculations in verifyBlockFees * Bound effectiveTipPerGas to 0 * Fix substrate-based fees * Overestimate delegation count weight hint * Use legacy txns and expect full gas_price to be paid as fee * Use constant for gas limit value * Start test case for max possible fee conditions * Clean up * Add runtime-upgrade test * prettier * First look at max possible fee * fix auto-compound delegation tx order flakiness * prettier * Remove cargo override (oops) * Hack to fix race condition * prettier * Use beforeEach for setting max multiplier * Test multiplier against Fibonacci contract * prettier * prettier * fmt * Various minor fixes * Add some fee multiplier scenarios * Don't use EXTRINSIC_BASE_WEIGHT in gas calc * Don't expect genesis value at each beforeAll * Bump expectation of CREATE cost * Slightly change length fee assumptions * Use higher gas price * Remove base_fee test * fmt * toml sort * Resolve compiler warnings * More compiler warning fixes * Fix receipt/status test * Remove irrelevant tests * Use maxDelegationCount for weight hint * Remove comment * Remove ignored tests * Move fee tests to integration_tests.rs * Re-remove ignored test * Move moonbeam runtime fee tests to integration_tests * Move moonriver runtime fee tests to integration_tests * fmt * Use base fee constant for gas price * Revert test name change... Co-authored-by: Nisheeth Barthwal <[email protected]> Co-authored-by: Crystalin <[email protected]> Co-authored-by: tgmichel <[email protected]>
What does it do?
This PR makes several significant fee modifications:
max_fee_per_gas
and replace it with a fixed1 Gwei
.transaction-payment
's multiplier. While the multiplier has a slightly different impact on the two fee mechanisms, the trend is very similar.transaction-payment
multiplier algorithm so that it should (1) remain within a useful range and (2) update quickly (tuned roughly to double within one hour when blocks are completely full).0.1
and max100_000
.1 Gwei
gas price (base_fee
) removed in favor ofpallet-transaction-payment
's congestion-based multiplier (acts similarly to EIP-1559)base-fee
, this is125_000_000
and125_000_000_000_000
respectively)Notes on compatibility with EIP-1559:
EIP-1559
. While our fee mechanism was never compatible and wasn't expected to be strictly compatible, this is a conscious deviation from its entire fee update mechanism.base-fee
and a txnmax_fee_per_gas
andmax_priority_fee_per_gas
, we will deduct the same fee as Ethereum will under EIP-1559.base_fee
is burned and any applicablepriority_fee
is paid to miners. In Moonbeam we still burn 80% and pay 20% to the treasury for both fees. No part of fees are paid to block authors currently.base_fee
is very different than EIP-1559. The specifics of the algorithm can be found in pallet transaction-payment's documentation.Notes on how this multiplier impacts Substrate and Ethereum fees differently:
gas_used
whereas it applies only to theweight
portion of Substrate fees.gas_used * 0
, basically).Analysis
The fees converge as the network becomes more congested (logarithmic scale). If the network remains empty, then the fees are also held at a constant value.
The following table exhibits the effect of the multiplier starting from
0.000003
(currently in moonriver) to increasing magnitudes. We see that the substrate and evm fees converge on higher multiplier values but substrate fees will always be higher than evm fees by a factor ofbase + length
fees.From the above table the range of
1.0 - 200.0
seems good so far.TODO
GenesisConfig
intransaction-payment